package kino.command;

import kino.aof.AofService;
import kino.client.KinoClient;
import kino.common.KinoConstant;
import kino.net.CommandEncoder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;

import java.util.HashMap;
import java.util.Map;

public abstract class AbstractKinoCommand implements KinoCommand {

    @Autowired
    protected CommandEncoder commandEncoder;

    @Autowired
    private AofService aofService;

    /**
     * 命令执行计数
     */
    private Integer executeCount = 0;

    @Override
    public final void execute(KinoClient client, String... argv) {
        // 命令执行起始时间
        Long startTime = System.currentTimeMillis();
        // 绑定命令与客户端
        client.bindCommand(this);
        // 校验参数
        ParamValidateResult paramValidateResult = this.doParamValidate(argv);

        // 判断命令是否校验过期key
        if (paramValidateResult.getSuccess() && this.expireCheck() && client.getDb().isExpire(argv[0])) {
            client.getDb().removeExpireAndClearKey(argv[0]);
        }
        // 需要转换扩展参数转换
        ExtParamParseResult extParamParseResult = null;
        if (paramValidateResult.getSuccess() && this.hasExtParam() && argv.length > this.getArgc()) {
            extParamParseResult = this.doExtParamParse(argv);
            // 转换成功加入客户端实例
            if (extParamParseResult.getSuccess()) {
                client.setExtParam(extParamParseResult.getExtParam());
            }
        }
        // 参数校验需要通过 并且 如果需要进行拓展参数转换则拓展参数转换与校验也需要通过 才进行命令执行
        if (paramValidateResult.getSuccess() && (extParamParseResult == null || extParamParseResult.getSuccess())){
            // 执行命令并把结果放入客户端输出缓冲区
            String executeResult = this.doExecute(client, argv);
            this.doResultAppend(client, executeResult);
            // 命令执行结果不是错误提示时记录aof
            if (!commandEncoder.isErrorMsg(executeResult)) {
                // 执行aof记录
                this.doAof(client);
            }
        } else if (!paramValidateResult.getSuccess()){
            // 参数校验错误
            this.doResultAppend(client, paramValidateResult.getMsg());
        } else if (extParamParseResult != null && !extParamParseResult.getSuccess()) {
            // 拓展参数转换与校验错误
            this.doResultAppend(client, extParamParseResult.getErrorMsg());
        }
        // 解绑命令与客户端
        client.unBindCommand();
        // 命令执行计数
        executeCount++;
        // 命令结束执行时间
        Long endTime = System.currentTimeMillis();
        // TODO 这里命令致性时间可以用做记录慢执行日志
    }


    public abstract String doExecute(KinoClient client, String... argv);
    public abstract Integer getArgc();
    public abstract String getCommandName();

    /**
     * 过期时间校验 如get等在执行命令的时候校验key是否过期，过期则进行过期key清除
     * @return
     */
    protected boolean expireCheck() {
        return false;
    }


    /**
     * aofFlag 返回 true代表该命令在执行完毕需要记录aof
     * @return
     */
    public abstract boolean aofFlag();

    public abstract boolean hasExtParam();

    protected void doAof(KinoClient client) {
        // 命令要求记录aof 且 客户端不是aof伪客户端
        if (this.aofFlag() && !client.isAofClient()) {
            aofService.aofAppend(client);
        }
    }
    public void doResultAppend(KinoClient client, String resultMsg) {
        client.resultAppend(resultMsg);
    }

    /**
     *  参数校验，如果命令存在扩展参数则 参数需要大于等于命令制定参数个数，如果不存在扩展参数则需要与命令指定参数个数相同
     * @param argv
     * @return
     */
    protected ParamValidateResult doParamValidate(String... argv) {
        if ((!this.hasExtParam() && argv.length != this.getArgc()) || (this.hasExtParam() && argv.length < this.getArgc())) {
            return new ParamValidateResult(false, KinoConstant.ARGC_ERR);
        }
        return new ParamValidateResult(true, null);
    }

    /**
     * 若命令存在扩展参数则需要重写该方法不然执行器拒绝执行
     * @param argv
     * @return
     */
    protected ExtParamParseResult doExtParamParse(String... argv) {
        ExtParamParseResult paramParseResult = new ExtParamParseResult();
        paramParseResult.setSuccess(false);
        paramParseResult.setErrorMsg(KinoConstant.EXT_ARGC_ERR);
        return paramParseResult;
    }

    public boolean isNumeric(String str){
        for (int i = str.length();--i>=0;){
            if (!Character.isDigit(str.charAt(i))){
                return false;
            }
        }
        return true;
    }
    class ExtParamParseResult {
        // 存放扩展参数
        private Map<String, Object> extParam;
        private String errorMsg;
        private Boolean success;

        public Boolean getSuccess() {
            return success;
        }

        public void setSuccess(Boolean success) {
            this.success = success;
        }

        public Map<String, Object> getExtParam() {
            return extParam;
        }

        public void setExtParam(Map<String, Object> extParam) {
            this.extParam = extParam;
        }

        public String getErrorMsg() {
            return errorMsg;
        }

        public void setErrorMsg(String errorMsg) {
            this.errorMsg = errorMsg;
        }
    }


    class ParamValidateResult {
        public ParamValidateResult(Boolean success, String msg) {
            this.msg = msg;
            this.success = success;
        }
        private Boolean success;
        private String msg;

        public Boolean getSuccess() {
            return success;
        }

        public void setSuccess(Boolean success) {
            this.success = success;
        }

        public String getMsg() {
            return msg;
        }

        public void setMsg(String msg) {
            this.msg = msg;
        }
    }
}
